package com.zhuiyun.project.api.wxlogin.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.tencentcloudapi.common.Credential;
import com.tencentcloudapi.common.exception.TencentCloudSDKException;
import com.tencentcloudapi.common.profile.ClientProfile;
import com.tencentcloudapi.common.profile.HttpProfile;
import com.tencentcloudapi.sms.v20210111.SmsClient;
import com.tencentcloudapi.sms.v20210111.models.SendSmsRequest;
import com.tencentcloudapi.sms.v20210111.models.SendSmsResponse;
import com.zhuiyun.project.api.wxlogin.config.TencentCloudAuth;
import com.zhuiyun.project.api.wxlogin.entiy.WxLoginVo;
import com.zhuiyun.project.api.wxlogin.entiy.WxUser;
import com.zhuiyun.project.api.wxlogin.mapper.WxLoginMapper;
import com.zhuiyun.project.api.wxlogin.service.WxLoginService;
import com.zhuiyun.project.api.appuser.model.AppUserModel;
import com.zhuiyun.project.api.appuser.entity.User;
import com.zhuiyun.project.api.appuser.mapper.AppUserMapper;
import com.zhuiyun.project.common.errorCode.EmErrorCode;
import com.zhuiyun.project.common.response.CommonResult;
import com.zhuiyun.project.security.jwt.JWTConfig;
import com.zhuiyun.project.security.jwt.JWTUtils;
import com.zhuiyun.project.util.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import tk.mybatis.mapper.entity.Example;

import java.util.*;


/**
 * @Description TODO 小程序微信授权登录
 * @ClassName UserLoginServiceImpl
 * @Author HH
 * @Date 2023/4/3 18:22
 **/
@Service
@Slf4j
public class WxLoginServiceImpl implements WxLoginService {

    @Autowired
    WxLoginMapper wxLoginMapper;
    @Autowired
    AppUserMapper appUserMapper;
    /**
     * 默认名称前缀
     */
    private final static String DEFAULT_NICKNAME_PREFIX = "YL_";
    /**
     * 默认头像id
     */
    private final static Integer DEFAULT_IMAGE_ID = 788;

    /**
     * @param user
     * @Author kdj
     * @Description 用户首次登录
     * @Date 2023/6/17 20:03
     * @Return com.zhuiyun.project.api.appuser.entiy.User
     */
    @Override
    public User addUserFirst(User user) {
        //性别
        user.setUserSex(1);
        //生成默认名称
        user.setUserName(DEFAULT_NICKNAME_PREFIX + user.getUserPhone().substring(7, 11));
        //头像
        user.setImageId(DEFAULT_IMAGE_ID);
        //用户类型默认为用户
        user.setUserType(0);
        //禁言 注销 删除标志
        user.setProhibition(0);
        user.setLogOff("0");
        user.setDelFlag(0);
        //创建时间 修改时间
        user.setCreateTime(new Date());
        user.setUpdateTime(new Date());
        //添加
        int i = appUserMapper.insert(user);
        if (i < 0) {
            //添加失败
            user = null;
        }
        return user;
    }

    /**
     * @Description 获取openid
     * @Author HH
     * @Date 2023/4/11 8:28
     * @Return java.lang.String
     */
    @Override
    public CommonResult getOpenid(String code) {
        JSONObject jsonObject = WXUtils.getOpenid(code);
        if (ObjectUtils.isNotEmpty(jsonObject)) {
            return CommonResult.ok(jsonObject);
        } else {
            return CommonResult.error(EmErrorCode.ID_NOT_OBJECT);
        }
    }


    /**
     * @param user
     * @Description 小程序微信授权用户登录
     * @Author HH
     * @Date 2023/4/10 18:47
     * @Return com.zhuiYun.project.common.response.CommonResult
     */
    @Override
    @Transactional
    public CommonResult login(AppUserModel user) {
        //获取openId
        String openId = user.getOpenId();
        User userInfo = new User();
        //将model转换成user
        BeanUtils.copy(user, userInfo);
        //根据手机号查询用户信息
        userInfo = this.getUserByPhone(user.getUserPhone());
        //未注册过，登录
        if (!ObjectUtils.isNotEmpty(userInfo)) {
            userInfo = new User();
            userInfo.setUserPhone(user.getUserPhone());
            //首次登录
            userInfo = this.addUserFirst(userInfo);
            //判断是否添加成功
            if (userInfo == null) {
                return CommonResult.error(EmErrorCode.USER_ADD_LOGIN);
            }
        }
        WxUser wxUser = this.queryWxUserByOpenId(openId);
        //登录的userId
        Integer userId = userInfo.getId();
        //判断openId是否存在
        if (ObjectUtils.isNotEmpty(wxUser)) {
            wxUser.setLastUserId(userId);
            //判断该user是否曾经在该微信号上登录过
            String userIds = wxUser.getUserId();
            //分割
            String[] userIdArr = userIds.split(",");
            //遍历
            long count = Arrays.stream(userIdArr).filter(s -> s.equals(userId.toString())).count();
            //存在
            if (count < 1) {
                userIds = userIds + "," + userInfo.getId();
                wxUser.setUserId(userIds);
            }
            //修改
            if (wxLoginMapper.updateByPrimaryKeySelective(wxUser) < 0) {
                return CommonResult.error(EmErrorCode.USER_ADD_LOGIN);
            }
            //存在
        } else {
            //赋值
            wxUser =new WxUser();
            wxUser.setOpenId(openId);
            wxUser.setLastUserId(userId);
            wxUser.setDelFlag(0);
            wxUser.setCreateTime(new Date());
            wxUser.setUpdateTime(new Date());
            wxUser.setUserId(userInfo.getId().toString());
            //创建
            if (wxLoginMapper.insert(wxUser) < 0) {
                return CommonResult.error(EmErrorCode.USER_ADD_LOGIN);
            }
        }
        // 生成token
        String token = JWTUtils.createAccessToken(userInfo,openId);
        // 设置token过期时间
        RedisUtil.set(token,userInfo,JWTConfig.expiration);
        RedisUtil.set(userInfo.getUserPhone(),token,JWTConfig.expiration);
        token = JWTConfig.tokenPrefix + token;
        HashMap<String,Object> hashMap = new HashMap<>();
        hashMap.put("imageId",userInfo.getImageId());
        hashMap.put("token", token);
        hashMap.put("userId", userInfo.getId());
        hashMap.put("userType", userInfo.getUserType());
        hashMap.put("userName", userInfo.getUserName());
        hashMap.put("userPhone", userInfo.getUserPhone());
        return CommonResult.ok(hashMap);
    }

    /**
     * @param phone
     * @Description 腾讯云发送手机短信
     * @Author HH
     * @Date 2023/4/12 9:43
     * @Return com.zhuiYun.project.common.response.CommonResult
     */
    @Override
    public CommonResult sendPhoneCode(String phone) {
        // 生成验证码
        String newCode = "";
        Random random = new Random();
        for (int i = 0; i < 6; i++) {
            newCode += random.nextInt(10);
        }
        boolean send = send(phone, newCode);
        log.info("send:{}", newCode);
        if (send) {
            // 查询redis有没有验证码
            String code = (String) RedisUtil.get("CACHE_PHONE_CODE" + phone);
            // 有的话先删除
            if (ObjectUtils.isNotEmpty(code)) {
                RedisUtil.del("CACHE_PHONE_CODE" + phone);
            }
            RedisUtil.set("CACHE_PHONE_CODE" + phone, newCode, 600);
            return CommonResult.ok(newCode);
        } else {
            return CommonResult.error(EmErrorCode.CODE_IS_ERROR);
        }
    }

    /**
     * @param phone
     * @param code
     * @Description 判断手机短信验证码是否正确
     * @Author HH
     * @Date 2023/4/12 13:56
     * @Return com.zhuiYun.project.common.response.CommonResult
     */
    @Override
    public CommonResult verificationCode(String phone, String code) {
        String redisCode = (String) RedisUtil.get("CACHE_PHONE_CODE" + phone);
        if (ObjectUtils.isNotEmpty(redisCode)) {
            if (redisCode.equals(code)) {
                return CommonResult.ok();
            } else {
                return CommonResult.error(EmErrorCode.CODE_IS_ERROR);
            }
        } else {
            return CommonResult.error(EmErrorCode.CODE_IS_ERROR);
        }
    }


    /**
     * @param wxLoginVo
     * @Author kdj
     * @Description 根据微信绑定的手机号快捷登录
     * @Date 2023/6/17 18:30
     * @Return com.zhuiyun.project.common.response.CommonResult
     */
    public CommonResult getPhoneNum(WxLoginVo wxLoginVo) {
        //获取手机号
        String phone = WXUtils.getPhoneNumber(wxLoginVo.getPhoneCode());
        log.info("手机号:{}", phone);
        return CommonResult.ok(phone);
    }


    /**
     * @param phone
     * @Author kdj
     * @Description 根据手机号查询用户信息
     * @Date 2023/6/19 12:10
     * @Return com.zhuiyun.project.api.appuser.entiy.User
     */
    @Override
    public User getUserByPhone(String phone) {
        Example example = new Example(User.class);
        if (ObjectUtils.isNotEmpty(phone)) {
            example.createCriteria().andEqualTo("userPhone", phone);
            return appUserMapper.selectOneByExample(example);
        }
        return null;
    }

    /**
     * @param openId
     * @Author kdj
     * @Description 根据openid判断wx用户是否存在
     * @Date 2023/6/19 16:00
     * @Return com.zhuiyun.project.api.wxlogin.entiy.WxUser
     */
    @Override
    public WxUser queryWxUserByOpenId(String openId) {
        Example example = new Example(WxUser.class);
        if (ObjectUtils.isNotEmpty(openId)) {
            example.createCriteria().andEqualTo("openId", openId);
            return wxLoginMapper.selectOneByExample(example);
        }
        return null;
    }


    /**
     * @param phone
     * @Description 腾讯云发送手机短信
     * @Author HH
     * @Date 2023/4/12 9:43
     * @Return com.zhuiYun.project.common.response.CommonResult
     */
    public boolean send(String phone, String code) {
        try {
            /* 必要步骤：
             * 实例化一个认证对象，入参需要传入腾讯云账户密钥对secretId，secretKey。
             * 这里采用的是从环境变量读取的方式，需要在环境变量中先设置这两个值。
             * 你也可以直接在代码中写死密钥对，但是小心不要将代码复制、上传或者分享给他人，
             * 以免泄露密钥对危及你的财产安全。
             * SecretId、SecretKey 查询: https://console.cloud.tencent.com/cam/capi */
            Credential cred = new Credential(TencentCloudAuth.SecretId, TencentCloudAuth.SecretKey);

            /** 实例化一个http选项，可选，没有特殊需求可以跳过 */
            HttpProfile httpProfile = new HttpProfile();
            /* 设置代理（无需要直接忽略）
             * httpProfile.setProxyHost("真实代理ip");
             * httpProfile.setProxyPort(真实代理端口); */

            /* SDK默认使用POST方法。
             * 如果你一定要使用GET方法，可以在这里设置。GET方法无法处理一些较大的请求 */
            httpProfile.setReqMethod("POST");
            /* SDK有默认的超时时间，非必要请不要进行调整
             * 如有需要请在代码中查阅以获取最新的默认值 */
            httpProfile.setConnTimeout(60);
            /* 指定接入地域域名，默认就近地域接入域名为 sms.tencentcloudapi.com ，也支持指定地域域名访问，例如广州地域的域名为 sms.ap-guangzhou.tencentcloudapi.com */
            httpProfile.setEndpoint("sms.tencentcloudapi.com");

            /* 非必要步骤:
             * 实例化一个客户端配置对象，可以指定超时时间等配置 */
            ClientProfile clientProfile = new ClientProfile();
            /* SDK默认用TC3-HMAC-SHA256进行签名
             * 非必要请不要修改这个字段 */
            clientProfile.setSignMethod("HmacSHA256");
            clientProfile.setHttpProfile(httpProfile);
            /* 实例化要请求产品(以sms为例)的client对象
             * 第二个参数是地域信息，可以直接填写字符串ap-guangzhou，支持的地域列表参考 https://cloud.tencent.com/document/api/382/52071#.E5.9C.B0.E5.9F.9F.E5.88.97.E8.A1.A8 */
            SmsClient client = new SmsClient(cred, "ap-guangzhou", clientProfile);
            /* 实例化一个请求对象，根据调用的接口和实际情况，可以进一步设置请求参数
             * 你可以直接查询SDK源码确定接口有哪些属性可以设置
             * 属性可能是基本类型，也可能引用了另一个数据结构
             * 推荐使用IDE进行开发，可以方便的跳转查阅各个接口和数据结构的文档说明 */
            SendSmsRequest req = new SendSmsRequest();

            /* 填充请求参数,这里request对象的成员变量即对应接口的入参
             * 你可以通过官网接口文档或跳转到request对象的定义处查看请求参数的定义
             * 基本类型的设置:
             * 帮助链接：
             * 短信控制台: https://console.cloud.tencent.com/smsv2
             * 腾讯云短信小助手: https://cloud.tencent.com/document/product/382/3773#.E6.8A.80.E6.9C.AF.E4.BA.A4.E6.B5.81 */

            /* 短信应用ID: 短信SdkAppId在 [短信控制台] 添加应用后生成的实际SdkAppId，示例如1400006666
             * 应用 ID 可前往 [短信控制台](https://console.cloud.tencent.com/smsv2/app-manage) 查看 */
            req.setSmsSdkAppId(TencentCloudAuth.sdkAppId);

            /* 短信签名内容: 使用 UTF-8 编码，必须填写已审核通过的签名
             * 签名信息可前往 [国内短信](https://console.cloud.tencent.com/smsv2/csms-sign) 或 [国际/港澳台短信]
             * (https://console.cloud.tencent.com/smsv2/isms-sign) 的签名管理查看 */
            String signName = TencentCloudAuth.signName;
            req.setSignName(signName);

            /* 模板 ID: 必须填写已审核通过的模板 ID 1761869
             * 模板 ID 可前往 [国内短信](https://console.cloud.tencent.com/smsv2/csms-template) 或 [国际/港澳台短信]
             * (https://console.cloud.tencent.com/smsv2/isms-template) 的正文模板管理查看 */
            String templateId = TencentCloudAuth.templateId;
            req.setTemplateId(templateId);

            /** 模板参数: 模板参数的个数需要与 TemplateId 对应模板的变量个数保持一致，若无模板参数，则设置为空 */
            String[] templateParamSet = {code};
            req.setTemplateParamSet(templateParamSet);

            /* 下发手机号码，采用 E.164 标准，+[国家或地区码][手机号]
             * 示例如：+8613711112222， 其中前面有一个+号 ，86为国家码，13711112222为手机号，最多不要超过200个手机号 */
            String[] phoneNumberSet = {"+86" + phone};
            req.setPhoneNumberSet(phoneNumberSet);



            /* 通过 client 对象调用 SendSms 方法发起请求。注意请求方法名与请求对象是对应的
             * 返回的 res 是一个 SendSmsResponse 类的实例，与请求对象对应 */
            SendSmsResponse res = client.SendSms(req);

            /** 输出json格式的字符串回包 */
            System.out.println(SendSmsResponse.toJsonString(res));
            return true;

            /* 也可以取出单个值，你可以通过官网接口文档或跳转到response对象的定义处查看返回字段的定义
             * System.out.println(res.getRequestId()); */


        } catch (TencentCloudSDKException e) {
            e.printStackTrace();
        }
        return false;
    }
}
